/*
 * Copyright (c) 2016 Fingerprint Cards AB <tech@fingerprints.com>
 *
 * All rights are reserved.
 * Proprietary and confidential.
 * Unauthorized copying of this file, via any medium is strictly prohibited.
 * Any use is subject to an appropriate license granted by Fingerprint Cards AB.
 */

#ifndef FPC_LOG_H
#define FPC_LOG_H

/**
 * @file   fpc_log.h
 * @brief  Debug log of text and data for an embedded application.
 *
 * This is the interface of a debug log component.
 * The services provided are:
 *   - Log numbering
 *   - Log timestamping
 *   - Log filtering
 *   - Consistent log formatting with buffer overflow checks
 *   - Execution decoupling from HW interface output (non-blocking)
 *   - Log queueing with controlled overwrite at full queue
 *   - Controlled shutdown awaiting HW interface output completed (blocking)
 *   - Modest RAM usage by late expansion of text strings, where viable
 *   - Conditional inclusion of log functions
 */

#include "fpc_log_config.h" // FPC_LOG_INCLUDE
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

/** @brief Log severity level classification.
 *
 * This is a generic classification of debug logs.
 * Some guidelines for use are given below.
 */
typedef enum
{
    /**
     * An event that may indicate permanent loss of function.
     * Example: Failed initialization of device.
     */
    FPC_LOG_LEVEL_ALERT   = 0,

    /**
     * An serious event that is believed to be corrected by program restart.
     * Example: Program functional supervision triggered.
     */
    FPC_LOG_LEVEL_ERROR   = 1,

    /**
     * An event that is probably caused from outside, but may indicate a problem.
     * Example: Unexpected loss of communication with peer node.
     */
    FPC_LOG_LEVEL_WARNING = 2,

    /**
     * An event that is expected but important enough to be part of a verbose log.
     * Example: Program restarted.
     */
    FPC_LOG_LEVEL_INFO    = 3,

    /** Free for arbitrary use. */
    FPC_LOG_LEVEL_DEBUG   = 4,

    FPC_LOG_LEVEL_LAST    = 4
} fpc_log_level_t;

#if (FPC_LOG_INCLUDE == 1)

/**
 * @brief Create a debug log.
 *
 * Puts a log record on queue for write to external interface. Asynchronous.
 *
 * Used for log of fixed message, without parameters.
 * Descriptions of this function (including parameters) is valid also for other fpc_log variants,
 * where not refined.
 *
 * @param[in] source Source of log record as a null terminated string.
 * E.g. name of reporting component. _Must_ be a constant string that is not altered during program
 * execution. The log component will truncate logged string controlled by FPC_LOG_MAX_SOURCE_SIZE.
 * @param[in] level Log level classification.
 * @param[in] message Log text as null terminated string.
 * _Must_ be a constant string that is not altered during program execution The log component
 * will truncate logged string controlled by FPC_LOG_MAX_MESSAGE_SIZE.
 *
 * Example use:
 *   fpc_log("fpc_sensor", FPC_LOG_LEVEL_ALERT, "Failed to read out ASIC id");
 */
void fpc_log(const char          *source,
             uint8_t              level,
             const char          *message);


/**
 * @brief Create a debug log.
 *
 * Used for log of message that may be variable (e.g. placed on stack) and for printf-like
 * formatting of log messages. The function handles message truncation. I.e. the caller need not
 * worry about buffer overflow.
 * @param[in] source
 * @param[in] level
 * @param[in] format Log text that may be altered during program execution and that may include
 * printf format directives (%).
 * @param[in] ... Optional parameters for formatting.
 */
void fpc_log_var(const char          *source,
                 uint8_t              level,
                 const char          *format,
                 ...);


/**
 * @brief Create a debug log.
 *
 * Used for log of fixed message with memory data of variable length. Memory data is written
 * as hex dump in log.
 *
 * @param[in] source
 * @param[in] level
 * @param[in] message
 * @param[in] data Memory data. May be NULL.
 * @param[in] len Length of user memory data. Ignored if data is NULL. The log component
 * will truncate logged hex dump controlled by FPC_LOG_MAX_DATA_SIZE.
 */
void fpc_log_mem(const char           *source,
                 uint8_t               level,
                 const char           *message,
                 const uint8_t        *data,
                 size_t                len);


/**
 * @brief Printf-like utility macro(s) to create a debug log.
 * The source of the log record will be the name of the calling function.
 * The log level classification will be "debug" or "error", for printf and
 * printfe respectively. The macro handles message truncation. I.e. the caller need
 * not worry about buffer overflow.
 *
 * NOTE
 * Will allocate FPC_LOG_MAX_MESSAGE_SIZE bytes on the caller's stack.
 */
//@{
#define fpc_log_printf(format, ...)                                                \
{                                                                                  \
    char fpc_log_buf[FPC_LOG_MAX_MESSAGE_SIZE];                                    \
                                                                                   \
    (void)snprintf(fpc_log_buf, FPC_LOG_MAX_MESSAGE_SIZE, format, ##__VA_ARGS__);  \
    fpc_log_var(__func__, FPC_LOG_LEVEL_DEBUG, fpc_log_buf);                       \
}

#define fpc_log_printfe(format, ...)                                               \
{                                                                                  \
    char fpc_log_buf[FPC_LOG_MAX_MESSAGE_SIZE];                                    \
                                                                                   \
    (void)snprintf(fpc_log_buf, FPC_LOG_MAX_MESSAGE_SIZE, format, ##__VA_ARGS__);  \
    fpc_log_var(__func__, FPC_LOG_LEVEL_ERROR, fpc_log_buf);                       \
}
//@}

/* Compatibility with FPC library components. */
//@cond FALSE
#define LOGD(format, ...) fpc_log_printf(format, ##__VA_ARGS__)
#define LOGE(format, ...) fpc_log_printfe(format, ##__VA_ARGS__)
#define LOGS(format, ...) fpc_log_printf(format, ##__VA_ARGS__)
//@endcond

/**
 * @brief Waits until all logs have been written to external interface.
 */
void fpc_log_flush(void);


/**
 * @brief Get current log level restriction.
 *
 * @return Current log level.
 */
uint8_t fpc_log_level_get(void);


/**
 * @brief Set log level restriction.
 *
 * Restricts log output based on log level.
 * Log records with severity less than specified log level will be discarded.
 * The log level severity is classified in #fpc_log_level_t.
 *
 * @param[in] level Requested log level restriction.
 */
void fpc_log_level_set(uint8_t level);


/**
 * @brief Initializes the log component.
 *
 * @param[in] level Initial log level restriction.
 */
void fpc_log_init(uint8_t level);


#else // FPC_LOG_INCLUDE

#define fpc_log(source, level, message)                                  ((void)0)
#define fpc_log_var(source, level, format, ...)                          ((void)0)
#define fpc_log_mem(source, level, message, data, len)                   ((void)0)
#define fpc_log_printf(format, ...)                                      ((void)0)
#define fpc_log_printfe(format, ...)                                     ((void)0)
#define LOGD(format, ...)                                                ((void)0)
#define LOGE(format, ...)                                                ((void)0)
#define LOGS(format, ...)                                                ((void)0)
#define fpc_log_flush()                                                  ((void)0)
#define fpc_log_level_get()                                              (0)
#define fpc_log_level_set(level)                                         ((void)0)
#define fpc_log_init(level)                                              ((void)0)

#endif // FPC_LOG_INCLUDE

/**
 * @brief Output string to diagnostic/trace interface
 *
 * This function sends a string to a suitable diagnostic/trace HW interface (ie ITM/UART)
 * It's used in bep lib for the trace and diagnostic functionality and is required
 * when using the diagnostic lib.
 *
 * @param[in] str String to send.
 */
void fpc_log_diag(const char *str);

#endif /* FPC_LOG_H */
